package controllers

import (
	"api_server/internal/models"
	"api_server/internal/services"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"github.com/gin-gonic/gin"
	"golang.org/x/sync/errgroup"
	"io/ioutil"
	common "jihe.common"
	"jihe.common/protos/base_server"
	base_server_proto "jihe.common/protos/base_server/proto"
	"jihe.common/protos/calculate_server"
	calculate_server_proto "jihe.common/protos/calculate_server/proto"
	"jihe.common/protos/order_server"
	order_server_proto "jihe.common/protos/order_server/proto"
	"jihe.common/protos/user_server"
	user_server_proto "jihe.common/protos/user_server/proto"
	"jihe.common/protos/wx_server"
	wx_server_proto "jihe.common/protos/wx_server/proto"
	"jihe.common/resp"
	"jihe.common/tool"
	"strings"
	"time"
)

type OrderController struct {
	Redis common.Redis
}

func (s *OrderController) CreateOrder(c *gin.Context) {
	preview := c.Query("preview")
	req := order_server_proto.OrderOrder{}
	err := c.BindJSON(&req)
	if err != nil {
		resp.RespParamErr(c)
		return
	}
	if req.NeedId == "" {
		resp.RespParamErr(c)
		return
	}
	dUid := getUid(c)
	var (
		g             errgroup.Group
		dUserInfo     *user_server_proto.UserUser
		needInfo      *base_server_proto.BaseCustomerRegistration
		fashionDetail *base_server_proto.BaseGetFashionDetailRes
		e             error
	)

	intoOrder := new(order_server_proto.OrderOrder)
	intoOrder.Id = fmt.Sprintf("%d", tool.GetSnowflakeId())
	g.Go(func() error {
		//查出设计师的折扣系数
		info, err := user_server.UserGetUserByIds(c, &user_server_proto.UserGetUserByIdsReq{Uids: []string{dUid}})
		if err != nil {
			return err
		}
		if len(info.List) == 0 {
			return fmt.Errorf("not fount user")
		}
		dUserInfo = info.List[0]
		return nil
	})
	g.Go(func() error {
		//根据需求ID查出需求详情
		info, err := base_server.BaseGetCustomerRegistrationDetail(c, &base_server_proto.BaseCustomerRegistration{Id: req.NeedId})
		if err != nil {
			return err
		}
		needInfo = info
		return nil
	})
	if err := g.Wait(); err != nil {
		resp.RespInternalErr(c, err.Error())
		return
	}
	if needInfo.DUid != dUid {
		resp.RespGeneralErr(c, "下单人与设计人不是同一人")
		return
	}
	//获取并算出面料系数
	fabricDiscount, err := services.GetFabricDiscount(req.NeedId)
	if err != nil {
		resp.RespInternalErr(c, err.Error())
		return
	}
	//查出款式详情
	fashionDetail, e = base_server.BaseGetFashionDetail(c, &base_server_proto.BaseGetFashionDetailReq{Id: needInfo.FashionId})
	if e != nil {
		resp.RespInternalErr(c, e.Error())
		return
	}
	qs, err := calculate_server.CalculateGetCutpartQueueByNeedId(c, &calculate_server_proto.CalculateCutpartQueue{
		NeedId: req.NeedId,
	})
	if err != nil {
		resp.RespInternalErr(c, err.Error())
		return
	}
	successNum := 0
	for _, v := range qs.List {
		if v.Status == 5 {
			successNum++
		}
	}
	if successNum != len(fashionDetail.BaseCutpartList) {
		resp.RespGeneralErr(c, "所有裁片计算完成才可下单")
		return
	}
	intoOrder.FashionId = fashionDetail.Fashion.Id
	intoOrder.FashionPrice = fashionDetail.Fashion.Price
	intoOrder.FashionName = fashionDetail.Fashion.Name
	intoOrder.NeedId = req.NeedId
	intoOrder.CUid = needInfo.CUid
	intoOrder.DUid = getUid(c)
	intoOrder.TempDiscount = req.TempDiscount
	intoOrder.FabricDiscount = int64(fabricDiscount)
	intoOrder.PayMethod = req.PayMethod
	intoOrder.PlatformDiscount = dUserInfo.PlatformDiscount
	intoOrder.Discount = dUserInfo.Discount
	intoOrder.TaxRatio = 5
	intoOrder.DiyPrice = req.DiyPrice
	basePrice := int64(float64(fashionDetail.Fashion.Price) * (float64(fabricDiscount) / float64(100)) * (float64(dUserInfo.PlatformDiscount) / float64(100)))

	//自定义价格
	if intoOrder.DiyPrice {
		intoOrder.Price = req.Price
		if intoOrder.Price < basePrice {
			resp.RespGeneralErr(c, "成交金额 >= 款式价格 * 面料系数 * 设计师平台折扣")
			return
		}
	} else {
		//成交金额 = 款式价格 * 面料系数 * 设计师自定系数 * 销售折扣(临时折扣)
		intoOrder.Price = int64(float64(fashionDetail.Fashion.Price) * (float64(fabricDiscount) / float64(100)) * (float64(dUserInfo.Discount) / float64(100)) * (float64(req.TempDiscount) / float64(100)))
	}

	//成交金额 >= 款式价格 * 面料系数 * 设计师平台折扣
	if intoOrder.Price < basePrice {
		resp.RespGeneralErr(c, fmt.Sprintf("成交金额 >= 款式价格 * 面料系数 * 设计师平台折扣 %d,%d", intoOrder.Price, basePrice))
		return
	}
	//设计税前提成（成交金额 - 款式价格 * 面料系数 * 设计师平台折扣）
	dTaxBefor := float64(intoOrder.Price) - float64(basePrice)
	//扣税、税钱
	intoOrder.TaxPrice = int64(dTaxBefor * (float64(intoOrder.TaxRatio) / float64(100)))
	//提成 = 税前提成 - 税钱
	intoOrder.RoyaltyPrice = int64(dTaxBefor - float64(intoOrder.TaxPrice))
	//平台收费(成交金额 - 设计师税后的钱)
	intoOrder.PlatformPrice = intoOrder.Price - intoOrder.RoyaltyPrice

	//如果是预览价格，这里直接返回
	if preview != "" {
		preRet := models.CreateOrderPreView{
			OrderOrder: intoOrder,
			BasePrice:  basePrice,
		}
		resp.RespOk(c, preRet)
		return
	}

	//重新上传copy3D场景基本信息
	baseInfo, err := services.CopyOrderBaseInfo(req.NeedId, intoOrder.Id)
	if err != nil {
		resp.RespInternalErr(c, err.Error())
		return
	}
	//复制款式工艺及兜位线信息
	//bucket, err := oss.GetClient().Bucket("jihe-file")
	//if err != nil {
	//	resp.RespInternalErr(c, err.Error())
	//	return
	//}
	interiorInfoUrl := fmt.Sprintf("%s/%s_crafts/%s.json?%d", common.Configer.Oss.Domain, common.Configer.Runmode, needInfo.FashionId, tool.GetSnowflakeId())
	_, interiorInfoBytes, errs := tool.GetRequest().Get(interiorInfoUrl).Timeout(time.Second * 10).EndBytes()
	if len(errs) != 0 {
		resp.RespInternalErr(c, errs[0].Error())
		return
	}
	interiorInfo := models.FashionCraft{}
	if len(interiorInfoBytes) != 0 {
		_ = json.Unmarshal(interiorInfoBytes, &interiorInfo)
		//if err != nil {
		//	resp.RespInternalErr(c, err.Error())
		//	return
		//}
	}
	cuts, err := base_server.BaseGetCutpartByNeedId(c, &base_server_proto.BaseNeedCutpart{NeedId: req.NeedId})
	if err != nil {
		resp.RespInternalErr(c, err.Error())
		return
	}
	baseInfo.ObjUrl, err = tool.ItemObjsMergeToObj(cuts.List, needInfo.ObjUrl)
	if err != nil {
		resp.RespInternalErr(c, err.Error())
		return
	}
	bgColors := make(map[string]string)
	_, bgdata, errs := tool.Request.Timeout(time.Second * 10).Get(fmt.Sprintf("%s/backgrounds/%s.json", common.Configer.Oss.Domain, req.NeedId)).EndBytes()
	if len(errs) != 0 {
		resp.RespInternalErr(c, errs[0].Error())
		return
	}
	_ = json.Unmarshal(bgdata, &bgColors)
	for _, v := range cuts.List {
		rgba := tool.GetRgba(bgColors[v.Code])
		if len(rgba) != 4 {
			rgba = []int{255, 255, 255, 1}
		}
		InteriorInfoByte, err := json.Marshal(interiorInfo.InteriorInfo[v.Code])
		if err != nil {
			resp.RespInternalErr(c, err.Error())
			return
		}
		baseInfo.ProductionMaterials = append(baseInfo.ProductionMaterials, &order_server_proto.ProductionMaterial{
			ObjPath:         v.AfterObj,
			UvWidth:         fashionDetail.Fashion.AlgorithmParam.UvWidth,
			Ratio:           v.Ratio,
			GreenUrl:        strings.Replace(v.ImgFace, "files.dizanz", "jihe-file.oss-cn-beijing-internal.aliyuncs", -1),
			PatternsUrl:     fmt.Sprintf("%s/patterns/%s/%s.json", common.Configer.Oss.Domain, intoOrder.Id, v.Code),
			R:               int64(rgba[0]),
			G:               int64(rgba[1]),
			B:               int64(rgba[2]),
			A:               int64(rgba[3]),
			Code:            v.Code,
			OrderId:         intoOrder.Id,
			Total:           int64(len(cuts.List)),
			InteriorInfo:    string(InteriorInfoByte),
			AfterDxfInfoUrl: v.AfterDxfInfoUrl,
		})
	}

	intoOrder.BaseInfo = &baseInfo

	createOrderResult, err := order_server.OrderCreate(c, intoOrder)
	if err != nil {
		resp.RespInternalErr(c, err.Error())
		return
	}
	resp.RespOk(c, createOrderResult)
}

func (s *OrderController) GetListByDuid(c *gin.Context) {
	req := order_server_proto.OrderGetListByDuidReq{}
	err := c.BindJSON(&req)
	if err != nil {
		resp.RespParamErr(c)
		return
	}
	if req.Page <= 0 {
		req.Page = 1
	}
	if req.PageSize <= 0 {
		req.PageSize = 20
	}
	if req.PageSize > 100 {
		resp.RespParamErr(c)
		return
	}
	req.DUid = getUid(c)
	orderInfo, err := order_server.OrderGetListByDuid(c, &req)
	if err != nil {
		resp.RespInternalErr(c, err.Error())
		return
	}
	if len(orderInfo.List) == 0 {
		resp.RespOk(c)
		return
	}
	cUids := make([]string, 0)
	cUidsMap := make(map[string]int)
	for _, v := range orderInfo.List {
		cUidsMap[v.CUid] = 1
	}
	for cuid, _ := range cUidsMap {
		cUids = append(cUids, cuid)
	}
	cUserInfos, err := user_server.UserGetCUsersByIds(c, &user_server_proto.UserGetCUsersByIdsReq{
		Uids: cUids,
	})
	if err != nil {
		resp.RespInternalErr(c, err.Error())
		return
	}
	retMap := make(map[string]interface{})
	retMap["cusers"] = cUserInfos.List
	retMap["list"] = orderInfo
	resp.RespOk(c, retMap)
}

func (s *OrderController) GetDetailByDuid(c *gin.Context) {
	id := c.Query("id")
	if id == "" {
		resp.RespParamErr(c)
		return
	}
	ret, err := order_server.OrderGetDetailById(c, &order_server_proto.OrderOrder{Id: id})
	if err != nil {
		if strings.Contains(err.Error(), "no rows") {
			resp.RespGeneralErr(c, "订单不存在")
			return
		}
		resp.RespInternalErr(c, err.Error())
		return
	}
	resp.RespOk(c, ret)
}

func (s *OrderController) DoPay(c *gin.Context) {
	orderId := c.Query("order_id")
	if orderId == "" {
		resp.RespParamErr(c)
		return
	}
	//用当前这个人的微信支付，不使用订单里的C端用户
	userInfo, err := user_server.UserGetByUid(c, &user_server_proto.UserDUser{Id: getUid(c)})
	if err != nil {
		resp.RespInternalErr(c, err.Error())
		return
	}
	orderInfo, err := order_server.OrderGetDetailById(c, &order_server_proto.OrderOrder{Id: orderId})
	if err != nil {
		resp.RespGeneralErr(c, "不存在的订单")
		return
	}
	ret, err := wx_server.WxDoPay(c, &wx_server_proto.WxDoPayReq{
		Name:      orderInfo.FashionName,
		TotalFee:  orderInfo.Price,
		NotifyUrl: common.Configer.Pay.WxCallback,
		Openid:    userInfo.Openid,
		OrderId:   orderInfo.Id,
	})
	if err != nil {
		resp.RespInternalErr(c, err.Error())
		return
	}
	resp.RespOk(c, ret)
}

type wxPayCallBack struct {
	OutTradeNo    string `xml:"out_trade_no"`
	TotalFee      int64  `xml:"total_fee"`
	TransactionId string `xml:"transaction_id"`
	ReturnCode    string `xml:"return_code"`
	ResultCode    string `xml:"result_code"`
	Openid        string `xml:"openid"`
}

func (s *OrderController) WxPayCallBack(c *gin.Context) {
	c.Writer.Header().Set("Content-Type", "application/xml; charset=utf-8")
	data, err := ioutil.ReadAll(c.Request.Body)
	if err != nil {
		resp.RespParamErr(c)
		return
	}
	common.Loger.Debug("WxPayCallBack:%s", string(data))
	req := wxPayCallBack{}
	err = xml.Unmarshal(data, &req)
	if err != nil {
		common.Loger.Error("WxPayCallBack err:%v data:%s", err, string(data))
		c.Writer.WriteString(`<xml>
		  <return_code><![CDATA[FAIL]]></return_code>
		</xml>`)
		return
	}
	if req.ResultCode != "SUCCESS" || req.ReturnCode != "SUCCESS" {
		common.Loger.Error("WxPayCallBack code is success data:%s", string(data))
		c.Writer.WriteString(`<xml>
		  <return_code><![CDATA[FAIL]]></return_code>
		</xml>`)
		return
	}
	redisClient := s.Redis.Get()
	orderId, _ := redisClient.Get(req.OutTradeNo).Result()
	if orderId == "" {
		common.Loger.Error("order_id is none data:%s", string(data))
		c.Writer.WriteString(`<xml>
		  <return_code><![CDATA[FAIL]]></return_code>
		</xml>`)
		return
	}
	redisClient.Del(req.OutTradeNo)
	//生成生产物料
	go func() {
		userInfo, err := user_server.UserGetByOpenid(c, &user_server_proto.UserDUser{Openid: req.Openid})
		if err != nil {
			common.Loger.Error("user not found data:%s", string(data))
			return
		}
		_, err = order_server.OrderUpdatePayComplete(c, &order_server_proto.OrderOrder{
			Id:            orderId,
			ThirdOrderNum: req.TransactionId,
			DealPrice:     req.TotalFee,
			PayUid:        userInfo.Id,
		})
		if err != nil {
			common.Loger.Error("update status err:%v data:%s", err, string(data))
			return
		}
		orderDetail, err := order_server.OrderGetDetailById(c, &order_server_proto.OrderOrder{Id: orderId})
		if err != nil {
			common.Loger.Error("order_server OrderGetDetailById err:%v data:%s", err, string(data))
			return
		}
		for _, v := range orderDetail.BaseInfo.ProductionMaterials {
			vB, _ := json.Marshal(v)
			err = s.Redis.Get().LPush(s.Redis.GetProductionMaterialConsume(), vB).Err()
			if err != nil {
				common.Loger.Error("redis LPush err:%v data:%s", err, string(data))
				return
			}
		}
	}()
	c.Writer.WriteString(`<xml>
	  <return_code><![CDATA[SUCCESS]]></return_code>
	  <return_msg><![CDATA[OK]]></return_msg>
	</xml>`)
	return
}

func (s *OrderController) CreateWithdraw(c *gin.Context) {
	req := order_server_proto.WithdrawRecord{}
	err := c.BindJSON(&req)
	if err != nil {
		resp.RespParamErr(c)
		return
	}
	req.Uid = getUid(c)
	if req.Bank == "" || req.BankNo == "" || req.RealName == "" {
		resp.RespParamErr(c)
		return
	}
	accountInfo, err := user_server.UserGetAmount(c, &user_server_proto.UserAccount{Uid: req.Uid})
	if err != nil {
		resp.RespInternalErr(c, err.Error())
		return
	}
	if accountInfo.TotalAmount <= 0 {
		resp.RespGeneralErr(c, "暂无可提现金额")
		return
	}
	req.Amount = accountInfo.TotalAmount
	_, err = user_server.UserAddAmount(c, &user_server_proto.UserAccount{Uid: req.Uid, TotalAmount: accountInfo.TotalAmount * -1})
	if err != nil {
		if strings.Contains(err.Error(), "user_accounts_total_amount_check") {
			resp.RespGeneralErr(c, "暂无可提现金额")
			return
		}
		resp.RespInternalErr(c, err.Error())
		return
	}
	//创建申请记录
	_, err = order_server.OrderCreateWithdrawRecord(c, &req)
	if err != nil {
		resp.RespInternalErr(c, err.Error())
		return
	}
	resp.RespOk(c)
}

func (s *OrderController) GetWithdrawInfo(c *gin.Context) {
	ret, err := order_server.OrderGetLastWithdrawRecord(c, &order_server_proto.WithdrawRecord{Uid: getUid(c)})
	if err != nil {
		if strings.Contains(err.Error(), "no rows") {
			resp.RespOk(c)
			return
		}
		resp.RespInternalErr(c, err.Error())
		return
	}
	resp.RespOk(c, ret)
}
